﻿using Common.Encrypt;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Common.Web
{
    /// <summary>
    /// 敏感数据序列化
    /// </summary>
    public class SensitiveDataConverter : Newtonsoft.Json.JsonConverter
    {
        /// <inheritdoc/>
        public override bool CanConvert(Type typeToConvert)
        {
            return true;
        }
        /// <inheritdoc/>
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            try
            {
                if (reader.TokenType == JsonToken.StartArray)
                {
                    //数组类型
                    var itemType = objectType.GetElementType();

                    //List类型
                    if (objectType.IsGenericType)
                    {
                        itemType = objectType.GetGenericArguments()[0];
                    }
                    StringBuilder builder = new StringBuilder("[");
                    while (reader.Read())
                    {
                        if (reader.TokenType == JsonToken.EndArray)
                        {
                            break;
                        }

                        object currentValue = reader.Value;
                        var itemValue = SensitiveDataController.Provider.Decode(currentValue);
                        var arrayValue = Convert.ChangeType(itemValue, itemType);
                        builder.Append("\"").Append(arrayValue).Append("\",");
                    }
                    if (builder.Length > 1)
                    {
                        builder.Remove(builder.Length - 1, 1);
                    }
                    builder.Append("]");
                    return JsonConvert.DeserializeObject(builder.ToString(), objectType);
                }
                else
                {
                    var currentValue = reader.Value;
                    if (currentValue == null)
                    {
                        return null;
                    }
                    var value = SensitiveDataController.Provider.Decode(currentValue);
                    if (string.IsNullOrEmpty(value + string.Empty))
                    {
                        return null;
                    }
                    var type = objectType;
                    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        type = Nullable.GetUnderlyingType(type);
                    }
                    return Convert.ChangeType(value, type);
                }
            }
            catch (SensitiveDataException ex)
            {
                string pathValue = $"(参数:{reader.Path}) 值:{reader.Value})";
                throw new SensitiveDataException(ex.Message + pathValue);
            }
            catch (Exception ex)
            {
                throw new Exception($"脱敏数据序列化失败(参数:{reader.Path}) 值:{reader.Value}", ex);
            }
        }

        /// <inheritdoc/>
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            string currentValue = value?.ToString();
            if (string.IsNullOrEmpty(currentValue))
            {
                serializer.Serialize(writer, null);
                return;
            }
            var type = value.GetType();
            if (type != typeof(string) && (type.IsArray || type.GetInterface(nameof(IEnumerable)) != null))
            {
                if (value is IEnumerable array)
                {
                    writer.WriteStartArray();
                    foreach (var item in array)
                    {
                        var itemValue = SensitiveDataController.Provider.Encode(item);
                        writer.WriteValue(itemValue);
                    }
                    writer.WriteEndArray();
                }
                return;
            }
            var sensitiveValue = SensitiveDataController.Provider.Encode(currentValue);
            serializer.Serialize(writer, sensitiveValue);

            //附加一个明文,暂时不支持数组类型
            string path = writer.Path;
            if (path.Contains("."))
            {
                path = path.Substring(path.LastIndexOf(".") + 1);
            }
            writer.WritePropertyName(path + "Text");
            writer.WriteValue(value.ToString());
        }
    }
}
